package com.homedo.async.mysql.support.mapper.insert;

import com.homedo.async.mysql.support.dml.DMLEnum;
import com.homedo.async.mysql.support.mapper.util.CreateFlux4PrimaryKeyResultUtil;
import com.homedo.async.mysql.support.mapper.util.CreateMono4PrimaryKeyResultUtil;
import com.homedo.async.mysql.support.meta.EntityMetaData;
import com.homedo.async.mysql.support.prepare.PrepareObject2Sql;
import com.homedo.async.mysql.support.prepare.PrepareObjectType;
import com.homedo.async.mysql.support.prepare.PrepareRecords2Sql;
import com.homedo.plainly.logger.Logger;
import com.homedo.plainly.logger.LoggerFactory;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.SqlConnection;
import org.reactivestreams.Publisher;
import org.springframework.util.Assert;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @author jgshun
 * @date 2020/3/17 10:41 AM
 */
public class SaveMapperImpl<T, ID> implements SaveMapperI<T, ID> {
    private static final Logger _LOGGER = LoggerFactory.getLogger(SaveMapperImpl.class);

    private Pool pool;

    private EntityMetaData<T> entityMetaData;

    public SaveMapperImpl(Pool pool, EntityMetaData<T> entityMetaData) {
        this.pool = pool;
        this.entityMetaData = entityMetaData;
    }


    @Override
    public <S extends T> Mono<S> saveSelective(S entity) {
        return this.save(entity, false, null);
    }

    @Override
    public <S extends T> Flux<S> saveSelective(Iterable<S> entities) {
        return this.save(entities, false, null);
    }

    @Override
    public <S extends T> Flux<S> saveSelective(Publisher<S> entityStream) {
        return this.save(entityStream, false, null);
    }

    @Override
    public <S extends T> Mono<S> save(S entity) {
        return this.save(entity, true, null);
    }

    @Override
    public <S extends T> Flux<S> save(Iterable<S> entities) {
        return this.save(entities, true, null);
    }

    @Override
    public <S extends T> Flux<S> save(Publisher<S> entityStream) {
        return this.save(entityStream, true, null);
    }

    //=======================

    @Override
    public <S extends T> Mono<S> saveSelective(S entity, SqlConnection connection) {
        return this.save(entity, false, connection);
    }

    @Override
    public <S extends T> Flux<S> saveSelective(Iterable<S> entities, SqlConnection connection) {
        return this.save(entities, false, connection);
    }

    @Override
    public <S extends T> Flux<S> saveSelective(Publisher<S> entityStream, SqlConnection connection) {
        return this.save(entityStream, false, connection);
    }

    @Override
    public <S extends T> Mono<S> save(S entity, SqlConnection connection) {
        return this.save(entity, true, connection);
    }

    @Override
    public <S extends T> Flux<S> save(Iterable<S> entities, SqlConnection connection) {
        return this.save(entities, true, connection);
    }

    @Override
    public <S extends T> Flux<S> save(Publisher<S> entityStream, SqlConnection connection) {
        return this.save(entityStream, true, connection);
    }

    private <S extends T> Mono<S> save(S entity, boolean includeNullVals, SqlConnection connection) {
        Assert.notNull(entity, "The given entity must not be null!");

        Object[] prepareSqlArgs = new PrepareObject2Sql(entity, entityMetaData, DMLEnum.INSERT, PrepareObjectType.ENTITY_RECORD, includeNullVals).prepare();

        return this.createSaveMono(prepareSqlArgs, entity, connection);
    }

    private <S extends T> Flux<S> save(Iterable<S> entities, boolean includeNullVals, SqlConnection connection) {
        Assert.notNull(entities, "The given Iterable of entity's must not be null!");

        Object[] prepareSqlArgs = new PrepareRecords2Sql(entityMetaData, DMLEnum.INSERT, entities, includeNullVals).prepare();

        return this.createSaveFlux(prepareSqlArgs, entities, connection);
    }

    private <S extends T> Flux<S> save(Publisher<S> entityStream, boolean includeNullVals, SqlConnection connection) {
        Assert.notNull(entityStream, "The given Publisher of entity's must not be null!");

        return Flux.from(entityStream).buffer().flatMap(e -> {
            return this.save(e, includeNullVals, connection);
        });
    }

    private <S extends T> Flux<S> createSaveFlux(Object[] prepareSqlArgs, Iterable<S> entities, SqlConnection connection) {
        return CreateFlux4PrimaryKeyResultUtil.create(prepareSqlArgs, entities, this.entityMetaData, this.pool, connection);
    }

    private <S extends T> Mono<S> createSaveMono(Object[] prepareSqlArgs, S entity, SqlConnection connection) {
        return CreateMono4PrimaryKeyResultUtil.create(prepareSqlArgs, entity, this.entityMetaData, this.pool, connection);
    }
}
